interface Button {
  btntype: string
  text: string
}
interface Link {
  alt: string
  href: string
}
interface Href {
  linktype: string
  target: Openlocation
}
enum Openlocation {
  self = 0,
  _blank,
  parent,
}
let button: Button = {
  btntype: 'normal',
  text: '跳转到百度',
}
let link: Link = {
  alt: 'goto baidu',
  href: 'http://www.baidu.com',
}
let href: Href = {
  linktype: '外网',
  target: Openlocation._blank,
}

// 使用 Extract来 优化 TS 泛型函数重载+交叉类型+泛型约束真实应用场景
type Extract<T, U> = T extends U ? T : never
// 改造
type CrosType<T> = Extract<T, object>

function cross<T, U>(
  objOne: CrosType<T>,
  objTwo: CrosType<U>
): T & U
function cross<T, U, V>(
  objOne: CrosType<T>,
  objTwo: CrosType<U>,
  objThree: CrosType<V>
): T & U & V

function cross<T, U, V>(objOne: T, objTwo: U, objThree?: V) {
  let obj = {}
  let combine = obj as T & U // 断言成 T 和 U  只允许添加 T 或 U 的属性 空对象可以像任何对象断言
  Object.keys(objOne).forEach((key) => {
    combine[key] = objOne[key]
  })
  Object.keys(objTwo).forEach((key) => {
    if (!obj.hasOwnProperty(key)) {
      combine[key] = objTwo[key]
    }
  })
  if (objThree) {
    let combine2 = combine as typeof combine & V
    Object.keys(objThree).forEach((key) => {
      if (!combine2.hasOwnProperty(key)) {
        combine2[key] = objThree[key]
      }
    })
    return combine2
  }
  return combine
}

let combine = cross(button, link, href)
// let combine2 = cross(combine, href)
console.log(combine)
// console.log(combine2)
/**
 * 此时是报错的 类型“string”的参数不能赋给类型“never”的参数。
 * 传递 string type Extract<T, U> = T extends U ? T : never
 * type Extract<string, object> = string extends object ? string : never
 * 所以 返回 never 是不符合 cross<string, string> 里面的泛型要求 报错
 * 这是个非常好的技巧性
 */
// cross<string, string>('button', 'link')
cross<Button, Link, Href>(button, link, href)
cross(button, link, href)
export {}
